"""
键盘按键监听记录器 - 全局后台监听版本
记录按键的按下和抬起时间，用于宏录制
支持后台监听，即使焦点不在本程序也能记录
"""
import tkinter as tk
from tkinter import scrolledtext, ttk, messagebox
from pynput import keyboard
import time
from datetime import datetime
import threading


class KeyboardRecorder:
    def __init__(self):
        self.window = tk.Tk()
        self.window.title("键盘按键记录器 - 全局后台监听")
        self.window.geometry("850x650")

        # 记录按键状态
        self.key_press_times = {}  # 存储每个按键的按下时间
        self.start_time = time.time()  # 程序启动时间作为基准
        self.is_recording = False
        self.recorded_events = []  # 存储录制的事件
        self.total_key_count = 0  # 总按键计数
        self.last_event_time = None  # 上一次事件的时间

        # 窗口置顶选项
        self.always_on_top = tk.BooleanVar(value=True)

        self._setup_ui()
        self._setup_keyboard_listener()

        # 默认窗口置顶
        self.window.attributes('-topmost', True)
        
    def _setup_ui(self):
        """设置用户界面"""
        # 顶部控制面板
        control_frame = tk.Frame(self.window, bg='#f0f0f0', pady=10)
        control_frame.pack(fill=tk.X)

        # 录制状态标签
        self.status_label = tk.Label(
            control_frame,
            text="状态: 未录制 | 后台监听中",
            font=('Arial', 12, 'bold'),
            bg='#f0f0f0',
            fg='red'
        )
        self.status_label.pack(side=tk.LEFT, padx=20)

        # 开始/停止按钮
        self.record_btn = tk.Button(
            control_frame,
            text="开始录制 (F9)",
            command=self.toggle_recording,
            bg='#4CAF50',
            fg='white',
            font=('Arial', 10, 'bold'),
            padx=20,
            pady=5
        )
        self.record_btn.pack(side=tk.LEFT, padx=5)

        # 清空按钮
        clear_btn = tk.Button(
            control_frame,
            text="清空记录",
            command=self.clear_log,
            bg='#ff9800',
            fg='white',
            font=('Arial', 10),
            padx=20,
            pady=5
        )
        clear_btn.pack(side=tk.LEFT, padx=5)

        # 导出按钮
        export_btn = tk.Button(
            control_frame,
            text="导出宏脚本",
            command=self.export_macro,
            bg='#2196F3',
            fg='white',
            font=('Arial', 10),
            padx=20,
            pady=5
        )
        export_btn.pack(side=tk.LEFT, padx=5)

        # 退出按钮
        exit_btn = tk.Button(
            control_frame,
            text="退出 (ESC)",
            command=self.on_closing,
            bg='#f44336',
            fg='white',
            font=('Arial', 10),
            padx=20,
            pady=5
        )
        exit_btn.pack(side=tk.RIGHT, padx=20)

        # 窗口置顶选项
        topmost_check = tk.Checkbutton(
            control_frame,
            text="窗口置顶",
            variable=self.always_on_top,
            command=self.toggle_topmost,
            bg='#f0f0f0',
            font=('Arial', 9)
        )
        topmost_check.pack(side=tk.RIGHT, padx=5)
        
        # 信息标签
        info_frame = tk.Frame(self.window, bg='#e3f2fd', pady=5)
        info_frame.pack(fill=tk.X)

        info_label = tk.Label(
            info_frame,
            text="✓ 全局后台监听已启用 | F9开始/停止录制 | ESC退出 | 即使焦点在其他程序也能记录",
            bg='#e3f2fd',
            font=('Arial', 9),
            fg='#0d47a1'
        )
        info_label.pack()
        
        # 日志显示区域
        log_frame = tk.Frame(self.window)
        log_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        tk.Label(log_frame, text="按键记录:", font=('Arial', 10, 'bold')).pack(anchor=tk.W)
        
        self.log_text = scrolledtext.ScrolledText(
            log_frame,
            wrap=tk.WORD,
            font=('Consolas', 10),
            bg='#1e1e1e',
            fg='#00ff00',
            insertbackground='white'
        )
        self.log_text.pack(fill=tk.BOTH, expand=True)
        
        # 统计信息
        stats_frame = tk.Frame(self.window, bg='#f5f5f5', pady=5)
        stats_frame.pack(fill=tk.X)

        self.stats_label = tk.Label(
            stats_frame,
            text="总按键次数: 0 | 录制事件: 0 | 监听状态: 全局后台监听",
            bg='#f5f5f5',
            font=('Arial', 9),
            fg='#2e7d32'
        )
        self.stats_label.pack()

    def toggle_topmost(self):
        """切换窗口置顶状态"""
        self.window.attributes('-topmost', self.always_on_top.get())
        
    def _setup_keyboard_listener(self):
        """设置键盘监听器"""
        self.listener = keyboard.Listener(
            on_press=self.on_key_press,
            on_release=self.on_key_release
        )
        self.listener.start()
        
    def toggle_recording(self):
        """切换录制状态"""
        self.is_recording = not self.is_recording
        if self.is_recording:
            self.status_label.config(text="状态: 正在录制 | 后台监听中", fg='green')
            self.record_btn.config(text="停止录制 (F9)", bg='#f44336')
            self.start_time = time.time()  # 重置基准时间
            self.last_event_time = None  # 重置上次事件时间
            self.recorded_events = []  # 清空之前的录制
            self.log_message("=" * 80, "info")
            self.log_message("▶ 开始录制宏... (可以切换到其他程序操作，我会在后台记录)", "info")
            self.log_message("=" * 80, "info")
        else:
            self.status_label.config(text="状态: 已停止 | 后台监听中", fg='red')
            self.record_btn.config(text="开始录制 (F9)", bg='#4CAF50')
            self.log_message("=" * 80, "info")
            self.log_message(f"■ 录制结束，共记录 {len(self.recorded_events)} 个事件", "info")
            self.log_message("=" * 80, "info")
    
    def get_key_name(self, key):
        """获取按键名称"""
        try:
            if hasattr(key, 'char') and key.char is not None:
                return key.char
            else:
                return str(key).replace('Key.', '')
        except:
            return str(key)
    
    def on_key_press(self, key):
        """按键按下事件 - 全局监听，后台也能记录"""
        try:
            # 处理F9切换录制（全局热键）
            if key == keyboard.Key.f9:
                self.window.after(0, self.toggle_recording)
                return

            # 处理ESC退出（全局热键）
            if key == keyboard.Key.esc:
                self.window.after(0, self.on_closing)
                return

            key_name = self.get_key_name(key)
            current_time = time.time()

            # 记录按下时间
            if key_name not in self.key_press_times:
                self.key_press_times[key_name] = current_time
                self.total_key_count += 1

                # 计算相对时间（毫秒）
                relative_time = (current_time - self.start_time) * 1000
                timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]

                # 计算距离上次操作的间隔
                interval_str = ""
                if self.last_event_time is not None:
                    interval = (current_time - self.last_event_time) * 1000
                    interval_str = f" | 距上次: {interval:6.0f}ms"
                else:
                    interval_str = " | 距上次: 首次操作"

                self.last_event_time = current_time

                message = f"[{timestamp}] [{relative_time:8.0f}ms] 按键: {key_name:15s} - 按下{interval_str}"
                self.window.after(0, lambda m=message: self.log_message(m, "press"))

                # 如果正在录制，添加到事件列表
                if self.is_recording:
                    self.recorded_events.append({
                        'type': 'press',
                        'key': key_name,
                        'time': relative_time,
                        'timestamp': timestamp
                    })
                    self.window.after(0, self.update_stats)

        except Exception as e:
            error_msg = f"错误: {str(e)}"
            self.window.after(0, lambda m=error_msg: self.log_message(m, "error"))
    
    def on_key_release(self, key):
        """按键抬起事件 - 全局监听，后台也能记录"""
        try:
            key_name = self.get_key_name(key)
            current_time = time.time()

            # 计算按键持续时间
            if key_name in self.key_press_times:
                press_time = self.key_press_times[key_name]
                duration = (current_time - press_time) * 1000  # 转换为毫秒
                relative_time = (current_time - self.start_time) * 1000
                timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]

                message = f"[{timestamp}] [{relative_time:8.0f}ms] 按键: {key_name:15s} - 抬起 (持续 {duration:.0f}ms)"
                self.window.after(0, lambda m=message: self.log_message(m, "release"))

                # 如果正在录制，添加到事件列表
                if self.is_recording:
                    self.recorded_events.append({
                        'type': 'release',
                        'key': key_name,
                        'time': relative_time,
                        'duration': duration,
                        'timestamp': timestamp
                    })
                    self.window.after(0, self.update_stats)

                # 清除按下时间记录
                del self.key_press_times[key_name]

        except Exception as e:
            error_msg = f"错误: {str(e)}"
            self.window.after(0, lambda m=error_msg: self.log_message(m, "error"))
    
    def log_message(self, message, msg_type="info"):
        """在日志区域显示消息"""
        self.log_text.insert(tk.END, message + "\n")
        self.log_text.see(tk.END)  # 自动滚动到底部
        
    def update_stats(self):
        """更新统计信息"""
        self.stats_label.config(
            text=f"总按键次数: {self.total_key_count} | 录制事件: {len(self.recorded_events)} | 监听状态: 全局后台监听"
        )
    
    def clear_log(self):
        """清空日志"""
        self.log_text.delete(1.0, tk.END)
        self.recorded_events = []
        self.update_stats()
        self.log_message("日志已清空", "info")
    
    def export_macro(self):
        """导出宏脚本"""
        if not self.recorded_events:
            self.log_message("没有可导出的录制数据！", "error")
            return
        
        filename = f"macro_{datetime.now().strftime('%Y%m%d_%H%M%S')}.py"
        
        with open(filename, 'w', encoding='utf-8') as f:
            f.write('"""\n')
            f.write(f'宏脚本 - 生成时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n')
            f.write(f'总事件数: {len(self.recorded_events)}\n')
            f.write('"""\n\n')
            f.write('from pynput.keyboard import Key, Controller\n')
            f.write('import time\n\n')
            f.write('keyboard = Controller()\n\n')
            f.write('def play_macro():\n')
            f.write('    """执行录制的宏"""\n')
            f.write('    start_time = time.time()\n\n')
            
            for event in self.recorded_events:
                wait_time = event['time'] / 1000  # 转换为秒
                key = event['key']
                
                # 处理特殊按键
                if len(key) > 1:
                    key_code = f"Key.{key}"
                else:
                    key_code = f"'{key}'"
                
                f.write(f'    # 等待到 {event["time"]:.0f}ms\n')
                f.write(f'    while (time.time() - start_time) < {wait_time:.3f}:\n')
                f.write(f'        time.sleep(0.001)\n')
                
                if event['type'] == 'press':
                    f.write(f'    keyboard.press({key_code})  # 按下 {key}\n\n')
                else:
                    f.write(f'    keyboard.release({key_code})  # 抬起 {key}\n\n')
            
            f.write('\nif __name__ == "__main__":\n')
            f.write('    print("开始执行宏...")\n')
            f.write('    time.sleep(2)  # 给你2秒准备时间\n')
            f.write('    play_macro()\n')
            f.write('    print("宏执行完成！")\n')
        
        self.log_message("=" * 60, "info")
        self.log_message(f"宏脚本已导出到: {filename}", "info")
        self.log_message("=" * 60, "info")
    
    def on_closing(self):
        """关闭窗口"""
        self.listener.stop()
        self.window.destroy()
    
    def run(self):
        """运行程序"""
        self.window.protocol("WM_DELETE_WINDOW", self.on_closing)
        self.log_message("=" * 70, "info")
        self.log_message("✓ 全局键盘监听器已启动！", "info")
        self.log_message("✓ 后台监听已启用 - 即使焦点在其他程序也能记录按键", "info")
        self.log_message("=" * 70, "info")
        self.log_message("快捷键说明:", "info")
        self.log_message("  F9  - 开始/停止录制宏", "info")
        self.log_message("  ESC - 退出程序", "info")
        self.log_message("-" * 70, "info")
        self.log_message("提示: 你可以切换到任何程序，我会在后台持续监听并记录", "info")
        self.log_message("-" * 70, "info")
        self.window.mainloop()


if __name__ == "__main__":
    recorder = KeyboardRecorder()
    recorder.run()

